package fem2.gui.src;

import vtk.vtkDataArray;
import vtk.vtkDoubleArray;
import vtk.vtkIdTypeArray;
import vtk.vtkPoints;
import vtk.vtkPolyData;
import fem2.Constraint;
import fem2.Element;
import fem2.Force;
import fem2.Load;
import fem2.Mesh;
import fem2.Model;
import fem2.Node;

public abstract class HofemSymbolsSource extends HofemSource {

	static class FieldInfo {

		/** Rotation in u_1 direction */
		public static final int BETA1 = 7;
		/** Rotation in u_2 direction */
		public static final int BETA2 = 6;
		/** Rotation about u_1 axis */
		public static final int R1 = 3;
		/** Rotation about u_2 axis */
		public static final int R2 = 4;
		/** Rotation about u_3 axis */
		public static final int R3 = 5;
		/** Displacement in u_1 direction */
		public static final int U1 = 0;
		/** Displacement in u_2 direction */
		public static final int U2 = 1;
		/** Displacement in u_3 direction */
		public static final int U3 = 2;
		/** Field names */
		private static final String[] names = { "u1", "u2", "u3", "r1", "r2", "r3", "beta 2",
				"beta 1" };

		/**
		 * Returns the name of the specified field.
		 * 
		 * @param id
		 *            field id
		 * @return name
		 */
		public static String getFieldName(int id) {
			return names[id];
		}
	}

	public static class BCSForceSource extends NFSymbolsSource {

		public BCSForceSource() {
			super(new int[] { FieldInfo.U1, FieldInfo.U2, FieldInfo.U3 });
		}
	}

	/** Distributed loads on elements (line, surface) */
	public static class BCSLoadSource extends HofemSymbolsSource {

		public BCSLoadSource() {
			super();
		}

		@Override
		protected void doExecute(vtkPoints points, vtkDataArray vectors) {
			Model model = modelHolder_.getModel();
			int nl = model.countLoads();

			for (int i = 0; i < nl; i++) {
				Load l = model.getLoad(i);
				int d = model.getMesh().countFields();
				Element e = model.getElement(0);

				if (l instanceof Force) {
					Force f = (Force) l;

					for (int j = 0; j < d; j++) {
						int fieldId = e.getFieldId(j);
						double value = f.getValue(j);

						if (value != 0.0) {
							int idx = fieldId % 3; // hack

							points.InsertNextPoint(f.getNode().getPosition());
							vectors.InsertNextTuple3(value * CARTESIAN_BASIS[idx][0], value
									* CARTESIAN_BASIS[idx][1], value * CARTESIAN_BASIS[idx][2]);
						}
					}
				}
			}
		}

		@Override
		protected vtkDataArray getVectors() {
			return new vtkDoubleArray();
		}

	}

	/** Concentrated moments acting on nodes */
	public static class BCSMomentSource extends NFSymbolsSource {

		public BCSMomentSource() {
			super(new int[] { FieldInfo.R1, FieldInfo.R2, FieldInfo.R3 });
		}
	}

	public static class BCSRotSource extends BCSymbolsSource {

		public BCSRotSource() {
			super(new int[] { FieldInfo.R1, FieldInfo.R2, FieldInfo.R3, FieldInfo.BETA2,
					FieldInfo.BETA1 });
		}
	}

	public static class BCSTransSource extends BCSymbolsSource {

		public BCSTransSource() {
			super(new int[] { FieldInfo.U1, FieldInfo.U2, FieldInfo.U3 });
		}
	}

	/** Boundary conditions */
	private abstract static class BCSymbolsSource extends HofemSymbolsSource {

		protected int[] dofs_;

		public BCSymbolsSource(int[] dofs) {
			dofs_ = dofs;
		}

		private boolean contains(int fieldId) {
			for (int d : dofs_) {
				if (d == fieldId) {
					return true;
				}
			}
			return false;
		}

		@Override
		protected void doExecute(vtkPoints pts, vtkDataArray vectors) {
			Model model = modelHolder_.getModel();
			Mesh m = model.getMesh();
			int nNodes = m.countNodes();

			for (int i = 0; i < nNodes; i++) {
				Node node = m.getNode(i);
				Constraint c = node.getConstraint();

				if (c != null) {
					insertConstraint(c, node.getPosition(), pts, vectors);
				}
			}

		}

		@Override
		protected vtkDataArray getVectors() {
			return new vtkIdTypeArray();
		}

		private void insertConstraint(Constraint c, double[] pt, vtkPoints pts, vtkDataArray vectors) {
			Model m = modelHolder_.getModel();
			int d = m.getMesh().countFields();
			Element e = m.getElement(0);

			for (int j = 0; j < d; j++) {
				int fieldId = e.getFieldId(j);

				if (!c.isFree(j) && contains(fieldId)) {
					// % 3 is a hack
					int idx = fieldId % 3;

					pts.InsertNextPoint(pt[0], pt[1], 0);
					vectors.InsertNextTuple3(CARTESIAN_BASIS[idx][0], CARTESIAN_BASIS[idx][1],
							CARTESIAN_BASIS[idx][2]);
				}
			}
		}

	}

	/** Nodal forces (forces, moments) */
	private abstract static class NFSymbolsSource extends HofemSymbolsSource {

		public NFSymbolsSource(int[] dofs) {
		}

		@Override
		protected void doExecute(vtkPoints pts, vtkDataArray vectors) {
		}

		@Override
		protected vtkDataArray getVectors() {
			return new vtkDoubleArray();
		}
	}

	// private static double[] LOAD_XIS = { -1, -2. / 3, -1. / 3, 0, 1. / 3, 2.
	// / 3, 1 };

	private static final double[][] CARTESIAN_BASIS = { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } };

	@Override
	protected void doExecute() {
		vtkPolyData outPD = GetPolyDataOutput();
		vtkPoints outPts = new vtkPoints();
		vtkDataArray outVectors = getVectors();

		outVectors.SetNumberOfComponents(3);
		outPD.SetPoints(outPts);
		outPD.GetPointData().SetVectors(outVectors);
		doExecute(outPts, outVectors);
	}

	protected abstract void doExecute(vtkPoints pts, vtkDataArray vectors);

	protected abstract vtkDataArray getVectors();

	@Override
	protected void modelChanged() {
		Modified();
	}

	@Override
	protected void solutionDataChanged() {
	}
}
